package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.*;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.dto.UsedTimesDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.NoteFormDTO;
import com.tianji.learning.domain.dto.UpdateNoteDTO;
import com.tianji.learning.domain.po.GatheredNote;
import com.tianji.learning.domain.po.TjNote;
import com.tianji.learning.domain.query.LearningNotePageQuery;
import com.tianji.learning.domain.query.NoteAdminPageQuery;
import com.tianji.learning.domain.vo.AdminNoteVO;
import com.tianji.learning.domain.vo.LearningNotePageVo;
import com.tianji.learning.domain.vo.NoteAdminVo;
import com.tianji.learning.mapper.TjNoteMapper;
import com.tianji.learning.service.IGatheredNoteService;
import com.tianji.learning.service.ITjNoteService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.codehaus.groovy.runtime.DefaultGroovyMethods.collect;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 */
@Slf4j
@Service
public class TjNoteServiceImpl extends ServiceImpl<TjNoteMapper, TjNote> implements ITjNoteService {
    private final RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
            HttpHost.create("http://192.168.137.128:9200")
    ));
    @Autowired
    private UserClient userClient;
    @Autowired
    private CatalogueClient catalogueClient;
    @Autowired
    private CourseClient courseClient;
    @Autowired
    private CategoryCache categoryCache;
    @Autowired
    private IGatheredNoteService gatheredNoteService;

    @Override
    public AdminNoteVO getAdminNote(Long id) {
        if (id == null) {
            throw new BizIllegalException("笔记ID不能为空");
        }
        //tj_note表id查询内容
        TjNote tjNote = this.getById(id);
        if (BeanUtil.isEmpty(tjNote)) {
            throw new BadRequestException("ID查询笔记不存在");
        }
        //封装对象
        AdminNoteVO adminNoteVO = BeanUtil.copyProperties(tjNote, AdminNoteVO.class);
        adminNoteVO.setId(id);
        //feign调用查询作者
        UserDTO userDTO = userClient.queryUserById(tjNote.getAuthorId().longValue());
        if (BeanUtil.isNotEmpty(userDTO)) {
            adminNoteVO.setAuthorName(userDTO.getName());
            adminNoteVO.setAuthorPhone(userDTO.getCellPhone());
        }
        //feign调用查询课程名称
        CourseFullInfoDTO fullCourseInfo = courseClient.getCourseInfoById(tjNote.getCourseId(), true, false);
        if (BeanUtil.isEmpty(fullCourseInfo)) {
            return adminNoteVO;
        }
        adminNoteVO.setCourseName(fullCourseInfo.getName());
        //解析章节名称
        List<CatalogueDTO> chapterList = fullCourseInfo.getChapters();
        CatalogueDTO chapter = chapterList.stream()
                .filter(c -> c.getId().equals(tjNote.getChapterId()))
                .findFirst()
                .orElse(null);
        if (BeanUtil.isNotEmpty(chapter)) {
            adminNoteVO.setChapterName(chapter.getName());
            CatalogueDTO section = chapter.getSections().stream()
                    .filter(c -> c.getId().equals(tjNote.getSectionId()))
                    .findFirst()
                    .orElse(null);
            if (BeanUtil.isNotEmpty(section)) {
                adminNoteVO.setSectionName(section.getName());
            }
        }
        //查询多级分类名称
        String name = categoryCache.getNameByLv3Id(fullCourseInfo.getThirdCateId());
        adminNoteVO.setCategoryNames(name);
        //采集人合集
        List<GatheredNote> gathers = gatheredNoteService.lambdaQuery()
                .eq(GatheredNote::getNoteId, id)
                .list();
        if (CollUtils.isNotEmpty(gathers)) {
            List<Long> gatherIds = gathers.stream().map(GatheredNote::getUserId).collect(Collectors.toList());
            List<UserDTO> userDTOS = userClient.queryUserByIds(gatherIds);
            if (CollUtils.isNotEmpty(userDTOS)) {
                List<String> gatherNames = userDTOS.stream().map(UserDTO::getName).collect(Collectors.toList());
                adminNoteVO.setGathers(gatherNames);
            }
        }
        return adminNoteVO;
    }

    private List<Long> getCourseId(String name) throws IOException {

        SearchRequest request = new SearchRequest("name");
        //封装查询条件
        BoolQueryBuilder queryBuilders = QueryBuilders.boolQuery();
        if (StringUtils.hasLength(name)) {
            queryBuilders.must(QueryBuilders.matchQuery("name", name));
        }

        request.source().query(queryBuilders);
        SearchResponse search = client.search(request, RequestOptions.DEFAULT);
        return parseResponse(search);
    }


    private List<Long> parseResponse(SearchResponse response) {

        // 解析响应结果
        SearchHits hits = response.getHits();
        List<CourseDTO> courseDTOS = new ArrayList<>();
        SearchHit[] hitss = hits.getHits();
        for (SearchHit hit : hitss) {
            String doc = hit.getSourceAsString();
            CourseDTO courseDTO = JSONUtil.toBean(doc, CourseDTO.class);
            courseDTOS.add(courseDTO);
        }
        return courseDTOS.stream().map(CourseDTO::getId).collect(Collectors.toList());
    }


    @Override
    public PageDTO<NoteAdminVo> adminPageQuery(NoteAdminPageQuery pageQuery) throws IOException {
        Page<TjNote> page = this.page(new Page<>(pageQuery.getPageNo(), pageQuery.getPageSize()));
        List<Long> ids = new ArrayList<>();
        if (pageQuery.getName() != null && !"".equals(pageQuery.getName())) {
            //通过es查询到 所有的id
            ids = getCourseId(pageQuery.getName());
        }

        //查询符合条件的所有TjNote
        List<TjNote> tjNotes = this.lambdaQuery().in(CollUtils.isNotEmpty(ids), TjNote::getCourseId, ids)
                .eq(pageQuery.getHidden() != null, TjNote::getHidden, pageQuery.getHidden())
                .le(pageQuery.getBeginTime() != null, TjNote::getCreateTime, pageQuery.getBeginTime())
                .ne(pageQuery.getEndTime() != null, TjNote::getCreateTime, pageQuery.getEndTime())
                .list();

        // 查询章节信息
        Map<Long, CataSimpleInfoDTO> catalogueMap = getCatalogueMap(tjNotes);
        // 查询课程信息
        Map<Long, CourseSimpleInfoDTO> courseMap = getCourseMap(tjNotes);
        //拿到作者信息
        Map<Long, UserDTO> userDTOMap = getUserMap(tjNotes);
        List<NoteAdminVo> list = new ArrayList<>();
        for (TjNote tjNote : tjNotes) {
            NoteAdminVo noteAdminVo = BeanUtil.copyProperties(tjNote, NoteAdminVo.class);
            //填充章名称
            noteAdminVo.setChapterName(catalogueMap.get(tjNote.getChapterId()).getName());
            //填充节名称
            noteAdminVo.setSectionName(catalogueMap.get(tjNote.getSectionId()).getName());
            //填充课程名
            noteAdminVo.setCourseName(courseMap.get(tjNote.getCourseId()).getName());
            // 填充作者名
            noteAdminVo.setAuthorName(userDTOMap.get(tjNote.getAuthorId()).getName());
            list.add(noteAdminVo);
        }
        return PageDTO.of(page, list);
    }

    private Map<Long, CourseSimpleInfoDTO> getCourseMap(List<TjNote> tjNotes) {

        List<Long> courseIds = tjNotes.stream().map(TjNote::getCourseId).distinct().collect(Collectors.toList());
        List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(courseList)) {
            log.info("查询不到课程信息：{}", courseIds);
            throw new BizIllegalException("网络异常");
        }
        Map<Long, CourseSimpleInfoDTO> courseMap = courseList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        return CollUtils.isEmpty(courseMap) ? new HashMap<>() : courseMap;


    }

    private Map<Long, UserDTO> getUserMap(List<TjNote> tjNotes) {
        List<Long> userIds = tjNotes.stream().map(TjNote::getAuthorId).collect(Collectors.toList());
        List<UserDTO> userDTOList = userClient.queryUserByIds(userIds);
        Map<Long, UserDTO> userDTOMap = userDTOList.stream().collect(Collectors.toMap(UserDTO::getId, Function.identity()));
        return userDTOMap;
    }

    private Map<Long, CataSimpleInfoDTO> getCatalogueMap(List<TjNote> tjNotes) {
        Map<Long, CataSimpleInfoDTO> catalogueMap = new HashMap<>();
        Set<Long> sectionIds = new HashSet<>();
        tjNotes.forEach(q -> {
            if (q.getChapterId() != null) {
                sectionIds.add(q.getChapterId());
            }
            if (q.getSectionId() != null) {
                sectionIds.add(q.getSectionId());
            }
        });
        if (CollUtils.isNotEmpty(sectionIds)) {
            List<CataSimpleInfoDTO> catalogues = catalogueClient.batchQueryCatalogue(sectionIds);
            if (CollUtils.isEmpty(catalogues)) {
                log.info("查询不到章节信息：{}", sectionIds);
                throw new BizIllegalException("网络异常");
            }
            catalogueMap = catalogues.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, Function.identity()));
        }
        return catalogueMap;
    }

    @Override
    public PageDTO<LearningNotePageVo> pageQuery(LearningNotePageQuery query) {
        Long userId = UserContext.getUser();
        List<TjNote> tjNoteList;
        LambdaQueryWrapper<TjNote> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(query.getCourseId() != null, TjNote::getCourseId, query.getCourseId())
                .eq(query.getSectionId() != null, TjNote::getSectionId, query.getSectionId())
                .eq(TjNote::getIsPrivate, false)
                .eq(TjNote::getHidden, false);
        //1.是否只查询我的笔记
        if (query.getOnlyMine()) {
//            //是，根据作者id查询
//            Long userId = UserContext.getUser();
//            wrapper.eq(userId != null, TjNote::getAuthorId, userId);
            //2.是否是我采集的笔记
            tjNoteList = this.baseMapper.selectNoteMy(UserContext.getUser());
        } else {
            //不是，查询所有
            tjNoteList = this.list(wrapper);
        }

        //2.是否是我采集的笔记，
        //3.查询出所有笔记和用户关系
        List<GatheredNote> gatheredNoteList = gatheredNoteService.lambdaQuery().eq(GatheredNote::getUserId, userId).list();
        List<Long> gatheredIds = gatheredNoteList.stream().map(GatheredNote::getNoteId).collect(Collectors.toList());
        List<LearningNotePageVo> learningNotePageVos = BeanUtil.copyToList(tjNoteList, LearningNotePageVo.class);
        if (CollUtils.isEmpty(tjNoteList)) {
            log.info("笔记表为空");
        }else {
            for (LearningNotePageVo learningNotePageVo : learningNotePageVos) {
                if (gatheredIds.contains(learningNotePageVo.getId())) {
                    learningNotePageVo.setIsGathered(true);
                }
            }
        }

        //4.根据作者id,批量查询作者信息
        List<Long> userIds = tjNoteList.stream().map(TjNote::getAuthorId).collect(Collectors.toList());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        learningNotePageVos.stream().forEach(vo -> {
            for (UserDTO userDTO : userDTOS) {
                if (ObjectUtils.equal(userDTO.getId(), vo.getAuthorId())) {
                    vo.setAuthorName(userDTO.getName());
                    vo.setAuthorIcon(userDTO.getIcon());
                }
            }
        });
        //5.分页
        Page<LearningNotePageVo> page = new Page<>(query.getPageNo(), query.getPageSize());
        return PageDTO.of(page, learningNotePageVos);
    }

    @Override
    public void saveNote(NoteFormDTO dto) {
        // 获取用户id
        Long userId = UserContext.getUser();
        // dto -> LearningNote
        TjNote note = BeanUtil.toBean(dto, TjNote.class);
        // 填充
        note.setAuthorId(userId);
        //
        note.setUsedTimes(0);
        // 保存
        this.save(note);
    }

    @Override
    public void updateNoteById(Long id, UpdateNoteDTO dto) {
        Long userId = UserContext.getUser();
        this.lambdaUpdate().set(TjNote::getContent,dto.getContent())
                .set(TjNote::getIsPrivate,dto.getIsPrivate())
                .eq(TjNote::getId,id)
                .eq(TjNote::getAuthorId,userId)
                .update();

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteNote(Long id) {
        Long userId = UserContext.getUser();
        LambdaQueryWrapper<TjNote> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TjNote::getId,id)
                .eq(TjNote::getAuthorId,userId);
        this.remove(wrapper);
        LambdaQueryWrapper<GatheredNote> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(GatheredNote::getNoteId,id);
        gatheredNoteService.remove(deleteWrapper);

    }

    @Autowired
    private RabbitMqHelper mqHelper;
    @Override
    public void gatherTjNote(Long id) {
        Long userId = UserContext.getUser();
        GatheredNote note = new GatheredNote();
        note.setUserId(userId);
        note.setNoteId(id);
        note.setIsGathered(true);
        gatheredNoteService.save(note);
        sendMqMsg(id);

    }

    @Override
    public void cancelTjNote(Long id) {
        Long userId = UserContext.getUser();
        GatheredNote note = gatheredNoteService.getOne(new QueryWrapper<GatheredNote>().eq("user_id", userId).eq("note_id", id));
        gatheredNoteService.removeById(note);
        sendMqMsg(id);

    }
    private void sendMqMsg(Long id){
        // 统计数量
        Integer count = gatheredNoteService.lambdaQuery()
                .eq(GatheredNote::getNoteId, id)
                .count();
        // 发送MQ
        UsedTimesDTO usedTimesDTO = new UsedTimesDTO(id, count);
        String key = MqConstants.Key.USED_TIMES_KEY_TEMPLATE;
        mqHelper.send(MqConstants.Exchange.GATHERED_NOTE_EXCHANGE, key, usedTimesDTO);
    }
}
